/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = ora_connect.c                                       */
/*  NOTE      =                                                    */
/*  DATE      = 2014/09/26 by songbb                               */
/*******************************************************************/

#include <stdio.h>
#include <memory.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/in.h>

#include "frcomm.h"
#include "frstr.h"
#include "frmem.h"
#include "frlog.h"

#include "conntrack.h"
#include "frct_comm.h"

#include "../ora_core.h"
#include "../comm/ora_comm.h"
#include "../comm/ora_util.h"
#include "ora_connect.h"
#include "../ttc/ora_mar_engine.h"

#define APM_ORA_SID_KEYS "DESCRIPTION.CONNECT_DATA.SID"
// 12c开始使用SERVICE_NAME表示SID
#define APM_ORA_SERVICE_NAME_KEYS "DESCRIPTION.CONNECT_DATA.SERVICE_NAME"
#define APM_ORA_PROGRAM_KEYS "DESCRIPTION.CONNECT_DATA.CID.PROGRAM"
#define APM_ORA_HOSTNAME_KEYS "DESCRIPTION.CONNECT_DATA.CID.USER"
// 使用jdbc时该字段始终是__jdbc__
#define ORA_HOST_KEYS "DESCRIPTION.CONNECT_DATA.CID.HOST"

#define ORA_REDIRECT_HOST "ADDRESS.HOST"
#define ORA_REDIRECT_PORT "ADDRESS.PORT"

/*******************************************************************************
*  FUNC     :  解析connect包，格式形如 (DESCRIPTION=(CONNECT_DATA=(SID=orcl)(CID=(PROGRAM=)(HOST=__jdbc__)(USER=)))(ADDRESS=(PROTOCOL=tcp)(HOST=172.16.0.74)(PORT=1521)))
*  ARGS     :  *p_st_conn                           (ct信息)
*           :  *p_st_sess                           (Oracle私有的session)
*           :  *p_st_ora_packet                     (Oracle数据包)
*  RTN      :  0                                    (FR_SUCC)
*              1                                    (FR_FAIL)
*  NOTE     :
*******************************************************************************/
int ora_connect_unpack(Conntrack *p_st_conn
                    , L7session_ora *p_st_sess
                    , L7_ora_packet *p_st_ora_packet)
{
    unsigned short us_tns_version;                  // tns version
    unsigned short us_connect_data_len;             // connect data length
    unsigned short us_connect_data_offset;          // connect data offset
    Frstr *p_ancestor_keys = NULL;
    Frlist_header *p_st_head = NULL;
    char *p_connect_data_str = NULL;
    char *p_sid = NULL;
    char *p_program = NULL;
    char *p_client_hostname = NULL;
    char *p_host = NULL;
    char *p_redirect_host = NULL;
    char *p_redirect_port = NULL;
    int i_exists_sid = FR_NO;
    int i_exists_program = FR_NO;
    int i_exists_client_hostname = FR_NO;
    unsigned short us_srv_value1inhardware;         // value of 1 in hardware

    if (!APM_ORA_HAS_PHASE_P_CONNECT(p_st_sess->ull_bits_phase_progress))
        APM_ORA_SET_PHASE_P_CONNECT(p_st_sess->ull_bits_phase_progress);
    else
        APM_ORA_SET_PHASE_P_RECONNECT(p_st_sess->ull_bits_phase_progress);

    bytebuff_setPosition(p_st_ora_packet->p_st_packet, APM_ORA_PKT_HEADER_LEN);      // 跳过包头

    if (bytebuff_get_short(p_st_ora_packet->p_st_packet, 2, &us_tns_version, BYTE_BIG_ENDIAN) != FR_SUCC)
        goto error;
    p_st_sess->s_tns_version_req = us_tns_version;

    if (ora_mar_skip(p_st_ora_packet, 12) != FR_SUCC)
        goto error;
    if (bytebuff_get_short(p_st_ora_packet->p_st_packet, 2, &us_srv_value1inhardware, BYTE_BIG_ENDIAN) != FR_SUCC)
        goto error;
    p_st_sess->i_cli_is_bigendian = us_srv_value1inhardware == 0x0001 ? FR_YES : FR_NO;
//    if (p_st_sess->i_use_special_field == FR_YES && p_st_sess->i_cli_is_bigendian == FR_YES)
//        p_st_sess->i_use_special_field = FR_NO;

    if (bytebuff_get_short(p_st_ora_packet->p_st_packet, 2, &us_connect_data_len, BYTE_BIG_ENDIAN) != FR_SUCC)
        goto error;

    if (bytebuff_get_short(p_st_ora_packet->p_st_packet, 2, &us_connect_data_offset, BYTE_BIG_ENDIAN) != FR_SUCC)
        goto error;

    if (ora_mar_check_bufflen_from(p_st_ora_packet, us_connect_data_offset, us_connect_data_len) != FR_SUCC)
        goto error;

    if ((p_connect_data_str = (char *) ora_malloc((us_connect_data_len + 1) * sizeof(char), bytebuff_remaining(p_st_ora_packet->p_st_packet) + 1)) == NULL)
    {
        ORA_MALLOCK_ERR;
        goto error;
    }

    if (bytebuff_get_bytes_at(p_st_ora_packet->p_st_packet, us_connect_data_offset, us_connect_data_len, p_connect_data_str) != FR_SUCC)
        goto error;
    p_connect_data_str[us_connect_data_len] = '\0';

    p_st_head = frlist_create();
    ORA_PRINT_WARN("connect_data:%s\n", p_connect_data_str);
    if (ora_NVPair_parser(p_connect_data_str, us_connect_data_len + 1, p_st_head, p_ancestor_keys) != FR_SUCC)
        goto error;

    // 取 SID
    ora_free((void **) &(p_st_sess->p_db_sid));
    if (ora_NVPair_get_value(p_st_head, APM_ORA_SID_KEYS, &p_sid) != FR_FAIL)
    {
        p_st_sess->p_db_sid = frstrdup(p_sid);
        i_exists_sid = FR_YES;
    }
    else if (ora_NVPair_get_value(p_st_head, APM_ORA_SERVICE_NAME_KEYS, &p_sid) != FR_FAIL)
    {
        p_st_sess->p_db_sid = frstrdup(p_sid);
        i_exists_sid = FR_YES;
    }
    else
    {
        ORA_PRINT_EMERG("can not find the keys of [%s] and [%s]\n", APM_ORA_SID_KEYS, APM_ORA_SERVICE_NAME_KEYS);
        ORA_FRLOG_ERROR("can not find the keys of [%s] and [%s]", APM_ORA_SID_KEYS, APM_ORA_SERVICE_NAME_KEYS);
        p_st_sess->p_db_sid = frstrdup("");
    }

    // 取程序名
    ora_free((void **) &(p_st_sess->p_program));
    if (ora_NVPair_get_value(p_st_head, APM_ORA_PROGRAM_KEYS, &p_program) != FR_FAIL)
    {
        p_st_sess->p_program = frstrdup(p_program);
        i_exists_program = FR_YES;
    }
    else
    {
        ORA_PRINT_EMERG("can not find the keys of [%s]\n", APM_ORA_PROGRAM_KEYS);
        ORA_FRLOG_ERROR("can not find the keys of [%s]\n", APM_ORA_PROGRAM_KEYS);
        p_st_sess->p_program = frstrdup("");
    }

    // 取主机名
    ora_free((void **) &(p_st_sess->p_client_hostname));
    if (ora_NVPair_get_value(p_st_head, APM_ORA_HOSTNAME_KEYS, &p_client_hostname) != FR_FAIL)
    {
        p_st_sess->p_client_hostname = frstrdup(p_client_hostname);
        i_exists_client_hostname = FR_YES;
    }
    else
    {
        ORA_PRINT_EMERG("can not find the keys of [%s]\n", APM_ORA_HOSTNAME_KEYS);
        ORA_FRLOG_ERROR("can not find the keys of [%s]\n", APM_ORA_HOSTNAME_KEYS);
        p_st_sess->p_client_hostname = frstrdup("");
    }

    // 查找host字段，如果该字段的内容是 __jdbc__，则表明使用的是jdbc驱动
    if (ora_NVPair_get_value(p_st_head, ORA_HOST_KEYS, &p_host) != FR_FAIL && strcmp(p_host, "__jdbc__") == 0)
    {
        ORA_PRINT_DBG("use jdbc driver\n");
        p_st_sess->i_use_oci = FR_NO;
        p_st_sess->i_use_special_field = FR_NO;
    }
    else
    {
        if (i_exists_sid == FR_NO && i_exists_program == FR_NO && i_exists_client_hostname == FR_NO &&
                ora_NVPair_get_value(p_st_head, ORA_REDIRECT_HOST, &p_redirect_host) == FR_SUCC &&
                ora_NVPair_get_value(p_st_head, ORA_REDIRECT_PORT, &p_redirect_port) == FR_SUCC)
        {
            /**
             * TODO: 经测试，9i jdbc重定向后的connect信息不完整，这里最好使用关联链路的信息进行设置
             * ./apm -n -w 8 -d ../../src/oracle/pcap/9i/jdbc/9.2.0.1.0_9.2.0.1.0_jdbc_thin_gb2312_long_all.pcap
             */
            ORA_PRINT_DBG("use jdbc driver\n");
            p_st_sess->i_use_oci = FR_NO;
            p_st_sess->i_use_special_field = FR_NO;
        }
        else
        {
            ORA_PRINT_DBG("use oci driver\n");
            p_st_sess->i_use_oci = FR_YES;
            if (p_st_sess->i_cli_is_bigendian == FR_YES)
                p_st_sess->i_use_special_field = p_st_sess->i_srv_is_bigendian == FR_YES ? FR_YES : FR_NO;
            else
                p_st_sess->i_use_special_field = FR_YES;
            ORA_PRINT_PARAM("i_use_special_field:[%s]\n", FR_YESNO2STR(p_st_sess->i_use_special_field));
        }
    }

    if (p_sid)
        ORA_PRINT_DBG("p_sid:[%s]\n", p_sid);
    if (p_program)
        ORA_PRINT_DBG("p_program:[%s]\n", p_program);
    if (p_client_hostname)
        ORA_PRINT_DBG("p_client_hostname:[%s]\n", p_client_hostname);
    if (p_host)
        ORA_PRINT_DBG("p_host:[%s]\n", p_host);

    frlist_destroy(p_st_head, ora_NVPair_list_node_free);

    ora_free((void **) &p_connect_data_str);
    return FR_SUCC;
error:
    ora_free((void **) &p_connect_data_str);
    frlist_destroy(p_st_head, ora_NVPair_list_node_free);
    return FR_FAIL;
}
